package com.syxbyi._1list;

import java.io.*;
import java.util.*;

/* a.使用 List 集合实现简易的学生信息管理系统，要求打印字符界面提示用户选择相应的功 能，根据用户输入的选择去实现增加、删除、修改、查找以及遍历所有学生信息的功能。

   b.自定义学号异常类和年龄异常类，并在该成员变量不合理时产生异常对象并抛出。

   c.当系统退出时将 List 集合中所有学生信息写入到文件中，当系统启动时读取文件中所 有学生信息到 List 集合中。
 */
public class StudentManagementSystem {

    // 使用commandList是为了使同一个命令能够拥有别名
    private final Map<String, Integer> commandList = new HashMap<>();
    // 因为学生有学号作为唯一id，所以很少使用列表编号来查找，因此使用LinkedList更高效些
    private List<Student> students = new LinkedList<>();
    private Scanner scanner;
    private final String dbPath = "./data/database";          // 数据库地址
    private final File database = new File(dbPath);

    public StudentManagementSystem() {
        load();
        // gen commands and its aliases
        commandList.put("help", -1);
        commandList.put("quit", 0);
        commandList.put("exit", 0);
        commandList.put("add", 1);
        commandList.put("modify", 2);
        commandList.put("find", 3);
        commandList.put("list", 4);
        commandList.put("delete", 5);
    }

    public void run() {
        scanner = new Scanner(System.in);
        String userInput= "";
        while(true) {
            userInput = getUserInput("> ");
            int commandId = whichCommand(userInput);
            if (commandId == 0) break;
            switch (commandId) {
                case 1:
                    add(); break;
                case 2:
                    modify(); break;
                case 3:
                    find(); break;
                case 4:
                    list(); break;
                case 5:
                    delete(); break;
                default:
                    printHelpInfo();
            }
        }
        save();
        scanner.close();
    }

    private int whichCommand(String input) {
        for (String s : commandList.keySet()) {
            if (input.equals(s))
                return commandList.get(s);
        }
        return -1;
    }

    /**
     * 输出命令行提示符，并读取用户输入
     * @param prompt    命令行提示符
     * @return  用户输入字符串
     */
    private String getUserInput(String prompt) {
        System.out.print(prompt);
        return scanner.nextLine().trim();
    }

    void printHelpInfo() {
        System.out.println("quit/exit: 退出");
        System.out.println("add: 添加学生信息");
        System.out.println("modify: 修改学生信息");
        System.out.println("delete: 删除学生信息");
        System.out.println("find: 查找学生信息");
        System.out.println("list: 列出所有学生");
    }

    /**
     * 生成若干新的学生信息。函数不断提示用户录入学生信息，并判断是否合法。输入一个空行会结束录入。
     * 应当只使用这个函数生成新的Student对象，以保证学号和年龄的合法性
     * @param message   录入输入之前的输出信息
     * @param prompt    命令行提示符
     * @return Student对象，如果用户输入了空行，则返回null
     */
    private Student getNewStudentInfo(String message, String prompt) {
        String id, name;
        int age;
        while (true) {
            System.out.println(message);
            String input = getUserInput(prompt);
            if (input.equals("")) return null;
            String[] inputArray = input.split("\\s+");
            try {
                id = inputArray[0];
                name = inputArray[1];
                age = Integer.parseInt(inputArray[2]);
                // 由于是添加新学生信息，这里需要检查学号是否重复
                if (checkStudentId(id) != -1) throw new StudentIdException("与已有学生学号重复");
                checkAge(age);
                break;
            } catch (ArrayIndexOutOfBoundsException e) {
                e.printStackTrace();
                System.out.println("输入项过少，每项信息间要使用空格分开");
            } catch (StudentIdException | AgeException e) {
                e.printStackTrace();
            }
        }
        return new Student(id, name, age);
    }

    /**
     * 修改已有学生的信息。提示用户录入学生信息，并判断是否合法。应当只使用这个函数修改Student对象，以保证年龄的合法性
     * @param message   录入输入之前的输出信息
     * @param prompt    命令行提示符
     * @param studentId 学生学号
     * @return  修改后的Student对象
     */
    private Student getStudentInfo(String message, String prompt, String studentId) {
        String name;
        int age;
        while (true) {
            System.out.println(message);
            String[] inputArray = getUserInput(prompt).split(" ");
            try {
                name = inputArray[0];
                age = Integer.parseInt(inputArray[1]);
                checkAge(age);
                break;
            } catch (ArrayIndexOutOfBoundsException e) {
                e.printStackTrace();
                System.out.println("输入项过少，每项信息间要使用空格分开");
            } catch (AgeException e) {
                e.printStackTrace();
            }
        }
        return new Student(studentId, name, age);
    }

    void add() {
        while (true) {
            Student student = getNewStudentInfo("请输入学生信息，格式为：学号 姓名 年龄（A111 小红 15）", "add> ");
            if (student == null) break;
            students.add(student);
            System.out.println("添加成功！输入一行空白可以结束添加");
        }
    }

    /**
     * 根据学号查找学生，修改学生信息（学号不可修改）
     */
    void modify() {
        list();
        String id;
        int index;
        while (true) {
            System.out.println("输入需要修改的学生的学号");
            try {
                id = getUserInput("modify> ");
                index = checkStudentId(id);
                break;
            } catch (StudentIdException e) {
                e.printStackTrace();
            }
        }
        Student student = getStudentInfo("请输入学生信息，格式为：姓名 年龄（小红 15）", "modify> ", id);
        students.set(index, student);
        System.out.println("修改成功");
    }

    /**
     * 查找一个学生，并打印该学生信息
     */
    void find() {
        int index;
        while (true) {
            System.out.println("输入要查找的学生学号");
            try {
                String id = getUserInput("find> ");
                index = checkStudentId(id);
                break;
            } catch (StudentIdException e) {
                e.printStackTrace();
            }
        }
        if (index == -1) {
            System.out.println("该学生不存在");
            return;
        }
        list(students.subList(index, index+1));
    }

    void list() {
        list(this.students);
    }

    void list(List<Student> studentList) {
        if (studentList.size() == 0) {
            System.out.println("没有学生");
        } else if (studentList.size() > 1) {
            System.out.println("学生总数" + studentList.size());
        }
        for (Student s: studentList) {
            System.out.println("学号：" + s.getId() + "\t姓名：" + s.getName() + "\t年龄：" + s.getAge());
        }

    }

    private void delete() {
        int index;
        while (true) {
            System.out.println("输入要删除的学生学号");
            try {
                String id = getUserInput("delete> ");
                index = checkStudentId(id);
                break;
            } catch (StudentIdException e) {
                e.printStackTrace();
            }
        }
        if (index == -1) {
            System.out.println("该学生不存在");
            return;
        }
        list(students.subList(index, index+1));
        System.out.println("确定删除该学生信息？Y/N");
        if (getUserInput("delete> ").equals("Y")) {
            students.remove(index);
            System.out.println("删除成功");
        }
    }

    /**
     * 检查学号是否合法，并检查系统中是否已经存有该学号
     * @param id    学生学号
     * @throws StudentIdException   如果学号不符合要求，则抛出学号异常
     * @return 返回该学号在系统list中的编号，如果未记录在系统中，则返回-1
     */
    int checkStudentId(String id) throws StudentIdException {
        // 假设id由一位大写字母和3位数字组成
        if (!id.matches("[A-Z]\\d{3}")) {
            throw new StudentIdException("学号必须由一位大写字母+3位数字组成");
        }
        int index = 0;
        for (Student s: students) {
            if (s.getId().equals(id))
                break;
            index++;
        }
        if (index < students.size())
            return index;
        return -1;
    }

    void checkAge(int age) throws AgeException {
        // 假设大家活到老学到老，100岁之前都可以做学生
        if (age <= 0) {
            throw new AgeException("你还没出生就开始学习了吗同学？");
        } else if (age >= 100) {
            throw new AgeException("震惊！百岁老人仍在努力学习！");
        }
    }

    private void load() {
        ObjectInputStream objectInputStream = null;
        if (!database.exists()) return;
        try {
            if (!database.isFile()) {
                throw new FileNotFoundException("Database已存在，但不是文件，请检查！");
            }
            objectInputStream = new ObjectInputStream(new FileInputStream(database));
            // 这里可能会有问题
            students = (List<Student>) objectInputStream.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
        try {
            if (objectInputStream != null)
                objectInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void save() {
        ObjectOutputStream objectOutputStream = null;
        try {
            if (!database.exists()) {
                System.out.println("Database文件不存在，创建中");
                if (database.createNewFile()) {
                    System.out.println("创建成功");
                } else {
                    System.out.println("创建失败");
                }
            }
            if (!database.isFile()) {
                throw new FileNotFoundException("Database已存在，但不是文件，请检查！");
            }
            objectOutputStream = new ObjectOutputStream(new FileOutputStream(dbPath));
            objectOutputStream.writeObject(students);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (objectOutputStream != null) {
                objectOutputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        StudentManagementSystem sms = new StudentManagementSystem();
        sms.run();
    }
}
